1 module unde.games.collision_detector;
2 
3 import std.algorithm.sorting;
4 import std.math;
5 import std.conv;
6 import std.string;
7 import std.stdio;
8 import std.algorithm.comparison;
9 
10 import unde.games.obj_loader;
11 enum Intersect
12 {
13     No = 0,
14     Almost = 1,
15     Yes = 2,
16     In = 3
17 }
18 
19 struct Vector
20 {
21     float x,y,z;
22 }
23 
24 void scene_to_collision_object (const (ObjFile) *sc, ref Vector[][string] output)
25 {
26     foreach (object; sc.objects)
27     {
28         uint i;
29         uint n = 0, t;
30         
31         string name = object.name;
32         Intersect intersect = Intersect.No;
33         
34         Vector[] vertices;
35         typeof(vertices) new_vertices;
36     
37         foreach (ref v; object.vertices)
38         {
39             vertices ~= Vector(-v[0], v[1], v[2]);
40         }
41     
42         //vertices.sort!("a.x < b.x || a.x==b.x && (a.y < b.y || a.y==b.y && a.z < b.z)");
43         vertices.sort!("a.x > b.x + 0.001 || abs(a.x-b.x) < 0.001 && (a.y > b.y + 0.001 || abs(a.y-b.y) < 0.001 && a.z > b.z + 0.001)");
44     
45         for (t = 1; t < vertices.length; t++)
46         {
47             if (!(abs(vertices[t-1].x-vertices[t].x) < 0.001 && abs(vertices[t-1].y-vertices[t].y) < 0.001 &&
48                   abs(vertices[t-1].z-vertices[t].z) < 0.001))
49             {
50                 new_vertices ~= Vector(
51                         vertices[t-1].x,
52                         vertices[t-1].y,
53                         vertices[t-1].z);
54             }
55         }
56     
57         new_vertices ~= Vector(
58                         vertices[$-1].x,
59                         vertices[$-1].y,
60                         vertices[$-1].z);
61         vertices = new_vertices;
62     
63         bool _join;
64         do
65         {
66             new_vertices = null;
67             _join = false;
68             if (name.startsWith("GroundGarden")) writefln("vertices.length = %s", vertices.length);
69             for (t = 1; t < vertices.length; t++)
70             {
71                 if (abs(vertices[t-1].x-vertices[t].x) < 0.001 && abs(vertices[t-1].y-vertices[t].y) < 0.001)
72                 {
73                     if (name.startsWith("GroundGarden")) writefln("x:%s,%s, y=%s,%s z:(%s+%s)/2=%s",
74                         vertices[t-1].x, vertices[t].x,
75                         vertices[t-1].y, vertices[t].y,
76                         vertices[t-1].z, vertices[t].z, (vertices[t-1].z+vertices[t].z)/2);
77                     new_vertices ~= Vector(
78                         (vertices[t-1].x+vertices[t].x)/2,
79                         (vertices[t-1].y+vertices[t].y)/2,
80                         (vertices[t-1].z+vertices[t].z)/2);
81                     _join = true;
82                     t++;
83                 }
84                 else new_vertices ~= vertices[t-1];
85                 
86                 if ( t+1 == vertices.length )
87                     new_vertices ~= vertices[t];
88             }
89             vertices = new_vertices;
90         } while (_join);
91     
92         for (t = 1; t < new_vertices.length; t++)
93         {
94             if (abs(new_vertices[t-1].y - new_vertices[t].y) < 0.001)
95             {
96                 float y = (new_vertices[t-1].y + new_vertices[t].y)/2;
97                 new_vertices[t-1].y = y;
98                 new_vertices[t].y = y;
99                 t++;
100             }
101         }
102     
103         for (t = 1; t < new_vertices.length; t++)
104         {
105             if (abs(new_vertices[t-1].x - new_vertices[t].x) < 0.001)
106             {
107                 float x = (new_vertices[t-1].x + new_vertices[t].x)/2;
108                 new_vertices[t-1].x = x;
109                 new_vertices[t].x = x;
110                 t++;
111             }
112         }
113     
114         output[name] = new_vertices;
115     }
116 }
117 
118 float vecLen(float[2] v)
119 {
120     return sqrt(v[0]^^2 + v[1]^^2);
121 }
122 
123 Intersect if_intersect (float[2] p1, float[2] p2, float[2] cp, float[2] p, bool _debug=false)
124 {
125     float[2] P2 = p2[] - p1[];
126     float[2] CP = cp[] - p[];
127 
128     float x, y;
129 
130     if (abs(P2[0]) < 0.001)
131     {
132         if (_debug) writefln("P2=%s, CP=%s", P2, CP);
133         x = p1[0];
134         if (CP[1] == 0.0)
135         {
136             y = cp[1];
137         }
138         else
139         {
140             y = (x - p[0])*CP[1]/CP[0] + p[1];
141         }
142     }
143     else if (abs(CP[1]) < 0.001)
144     {
145         if (_debug) writefln("P2=%s, CP=%s", P2, CP);
146         y = p[1];
147         x = (y - p1[1])*P2[0]/P2[1] + p1[0];
148     }
149     else
150     {
151         x = (p[0]/CP[0] - p1[0]*P2[1]/P2[0]/CP[1] + (p1[1] - p[1])/CP[1]) / (1/CP[0] - P2[1]/P2[0]/CP[1]);
152         y = (x - p1[0])*P2[1]/P2[0] + p1[1];
153     }
154 
155     float[2] cross = [x, y];
156     float[2] cr = p[] - cross[];
157     float[2] cd = cp[] - cross[];
158 
159     float lambda = vecLen(cr)/vecLen(cd);
160 
161     foreach (ref pp; cr)
162     {
163         if (abs(pp) < 0.01) pp = 0.0;
164     }
165 
166     for (int i; i < 2; i++)
167     {
168         if (cr[i] == 0.0 && abs(cd[i]) < 0.1) cd[i] = 0.0;
169     }
170 
171     if ( !(sgn(cr[0]) != sgn(cd[0]) &&
172         sgn(cr[1]) != sgn(cd[1])) &&
173         (sgn(cr[0]) != sgn(cd[0]) ||
174         sgn(cr[1]) != sgn(cd[1])) &&
175         cr[0] != 0.0 && cr[1] != 0.0 &&
176         cd[0] != 0.0 && cd[1] != 0.0)
177     {
178         if(_debug)
179         { 
180             writefln("%s!=%s, %s!=%s",
181                 sgn(cr[0]), sgn(cd[0]),
182                 sgn(cr[1]), sgn(cd[1]));
183             writefln("cr=%s, cd=%s", cr, cd);
184         }
185         if (_debug) writefln("OH %s, %s, %s, %s: cross=%s, lambda = %s", p1, p2, cp, p, cross, lambda);
186         return Intersect.Yes;
187     }
188 
189     if (sgn(cr[0]) != sgn(cd[0]) && cr[0] != 0.0 && cd[0] != 0.0 ||
190         sgn(cr[1]) != sgn(cd[1]) && cr[1] != 0.0 && cd[1] != 0.0) lambda = -lambda;
191 
192     if (_debug) writefln("%s, %s, %s, %s: cross=%s, lambda = %s (cr=%s, cd=%s)", p1, p2, cp, p, cross, lambda, cr, cd);
193     
194     if (lambda > 0.0) return Intersect.In;
195     else if (lambda == 0.0) return Intersect.Yes;
196     else return Intersect.No;
197 }
198 
199 long if_intersect (float[2] p1, float[2] p2, float[2] cp, float[4] box, bool _debug=false)
200 {
201     assert(p1 != p2);
202     assert(p1 != cp);
203     assert(p2 != cp);
204     
205     float[2][] points;
206     points ~= [ box[0], box[1] ];
207     points ~= [ box[0], box[3] ];
208     points ~= [ box[2], box[1] ];
209     points ~= [ box[2], box[3] ];
210 
211     Intersect max_i = Intersect.No;
212     Intersect min_i = Intersect.In;
213 
214     long inners;
215 
216     foreach(j, p; points)
217     {
218         auto i = if_intersect (p1, p2, cp, p, _debug);
219         if (i == Intersect.In)
220             inners |= 2^^j;
221         max_i = max(max_i, i);
222         min_i = min(min_i, i);
223     }
224 
225     if (_debug) writefln("Отрезок, точка и бокс. (%s, %s) %s, %s, %s, %s",
226         min_i, max_i, p1, p2, cp, box);
227 
228     if (max_i == min_i) return min_i;
229 
230     float[2] P2 = p2[] - p1[];
231     float x, y;
232     x = box[0];
233     if (p1[0] < x && x < p2[0] ||
234         p2[0] < x && x < p1[0])
235     {
236         y = (x - p1[0])*P2[1]/P2[0] + p1[1];
237         if (_debug) writefln("1. (%s, %s)", x, y);
238         if (box[1] < y && y < box[3])
239             return Intersect.Yes;
240     }
241 
242     x = box[2];
243     if (p1[0] < x && x < p2[0] ||
244         p2[0] < x && x < p1[0])
245     {
246         y = (x - p1[0])*P2[1]/P2[0] + p1[1];
247         if (_debug) writefln("2. (%s, %s)", x, y);
248         if (box[1] < y && y < box[3])
249             return Intersect.Yes;    
250     }
251 
252     y = box[1];
253     if (p1[1] < y && y < p2[1] ||
254         p2[1] < y && y < p1[1])
255     {
256         x = (y - p1[1])*P2[0]/P2[1] + p1[0];
257         if (_debug) writefln("3. (%s, %s)", x, y);
258         if (box[0] < x && x < box[2])
259             return Intersect.Yes;
260     }
261 
262     y = box[3];
263     if (p1[1] < y && y < p2[1] ||
264         p2[1] < y && y < p1[1])
265     {
266         x = (y - p1[1])*P2[0]/P2[1] + p1[0];
267         if (_debug) writefln("4. (%s, %s)", x, y);
268         if (box[0] < x && x < box[2])
269             return Intersect.Yes;
270     }
271 
272     if (box[0] < p1[0] && p1[0] < box[2] &&
273         box[1] < p1[1] && p1[1] < box[3] ||
274         box[0] < p2[0] && p2[0] < box[2] &&
275         box[1] < p2[1] && p2[1] < box[3])
276         return Intersect.Yes;
277 
278     if (_debug) writefln("no");
279     return -inners;
280 }
281 
282 string last_intersect;
283 
284 struct CollisionCacheEntry
285 {
286     string name;
287     size_t from;
288     size_t to;
289 }
290 
291 struct SC
292 {
293     int x, y;
294 }
295 
296 private CollisionCacheEntry[][SC][void *] collision_cache;
297 
298 void reset_collision_cache()
299 {
300     collision_cache = null;
301 }
302 
303 Intersect if_intersect (Vector[][string] co, float[6] box, bool _debug = false)
304 in
305 {
306     assert(box[0] < box[3], format("Box x0=%s must be less x1=%s", box[0], box[3]));
307     assert(box[1] < box[4]);
308     assert(box[2] < box[5]);
309 }
310 body
311 {
312     size_t j;
313 
314     Intersect intersect = Intersect.No;
315 
316     Intersect check_object(string name, Vector[] object, size_t from, size_t to,
317         void *co, SC *sc)
318     {
319         Intersect intersect = Intersect.No;
320 
321         size_t cfrom = object.length, cto = min(2, object.length);
322 
323         if (object.length < 3) return intersect;
324 
325         /*if (abs(object[0].y - box[1]) > 3.0 &&
326             abs(object[$-1].y - box[1]) > 3.0 &&
327             abs(object[$-1].y - object[0].y) < 3.0)
328             return intersect;*/
329         
330         for (j = from; j < to; j++)
331         {
332             auto v1 = object[j-2];
333             auto v2 = object[j-1];
334             auto v3 = object[j];
335             typeof(v3) v4;
336             if (j+1 < object.length) v4 = object[j+1];
337             
338             bool __debug = _debug;
339 
340             if (co)
341             {
342                 auto vl = j+1 < object.length?v4:v3;
343                 if (v1.x < (sc.x*30 - 20) || vl.x > (sc.x*30 + 20) ||
344                     v1.y < (sc.y*17 - 10) && vl.y < (sc.y*17 - 10) || 
345                     v1.y > (sc.y*17 + 10) && vl.y > (sc.y*17 + 10))
346                 {
347                     continue;
348                 }
349             
350                 if (j < cfrom) cfrom = j;
351                 if (j+1 > cto) cto = j+1;
352             }
353             
354             if (_debug && abs(v1.x - box[0]) > 3.0 && abs(v3.x - box[0]) > 3.0) __debug = false;
355             if (abs(v1.x - box[0]) > 3.0 && abs(v3.x - box[0]) > 3.0 &&
356                 abs(v3.x - v1.x) < 3.0) continue;
357 
358             if (v1.x == v2.x && v3.x == v4.x)
359             {
360                 float z = (v1.z + v2.z + v3.z + v4.z)/4;
361                 if (box[2] <= z && z <= box[5])
362                 {
363                     Intersect max_i = Intersect.No;
364                     Intersect min_i = Intersect.In;
365                     long inners = 2^^4-1;
366                     
367                     auto i = if_intersect ([v1.x, v1.y], [v2.x, v2.y], [v3.x, v3.y],
368                         [box[0], box[1], box[3], box[4]], __debug);
369                     Intersect inter;
370                     inter = Intersect.No;
371                     if (i > 0) inter = cast(Intersect) i;
372                     if (i <= 0) inners &= -i;
373                     max_i = max(max_i, inter);
374                     min_i = min(min_i, inter);
375     
376                     i = if_intersect ([v1.x, v1.y], [v3.x, v3.y], [v4.x, v4.y],
377                         [box[0], box[1], box[3], box[4]], __debug);
378                     inter = Intersect.No;
379                     if (i > 0) inter = cast(Intersect) i;
380                     if (i <= 0) inners &= -i;
381                     max_i = max(max_i, inter);
382                     min_i = min(min_i, inter);
383     
384                     i = if_intersect ([v2.x, v2.y], [v4.x, v4.y], [v1.x, v1.y],
385                         [box[0], box[1], box[3], box[4]], __debug);
386                     inter = Intersect.No;
387                     if (i > 0) inter = cast(Intersect) i;
388                     if (i <= 0) inners &= -i;
389                     max_i = max(max_i, inter);
390                     min_i = min(min_i, inter);
391     
392                     i = if_intersect ([v3.x, v3.y], [v4.x, v4.y], [v1.x, v1.y],
393                         [box[0], box[1], box[3], box[4]], __debug);
394                     inter = Intersect.No;
395                     if (i > 0) inter = cast(Intersect) i;
396                     if (i <= 0) inners &= -i;
397                     max_i = max(max_i, inter);
398                     min_i = min(min_i, inter);
399 
400                     if (__debug) writefln("%s. (%s, %s, %s) points: %s, %s, %s, %s, box: %s", name, 
401                         min_i, max_i, inners, v1, v2, v3, v4, box);
402     
403                     if (min_i > Intersect.No || inners > 0)
404                     {
405                         last_intersect = name;
406                         
407                         if (_debug) writefln("%s. %s", min_i, name);
408                         if (!co) return max(min_i, Intersect.Yes);
409                         else intersect = max(min_i, Intersect.Yes, intersect);
410                     }
411                 }
412                     
413                 j++;
414             }
415             else
416             {
417                 float z = (v1.z + v2.z + v3.z)/4;
418                 if (box[2] <= z && z <= box[5])
419                 {
420                     Intersect max_i = Intersect.No;
421                     Intersect min_i = Intersect.In;
422                     long inners = 2^^4-1;
423                     
424                     auto i = if_intersect ([v1.x, v1.y], [v2.x, v2.y], [v3.x, v3.y],
425                         [box[0], box[1], box[3], box[4]], __debug);
426                         
427                     Intersect inter;
428                     inter = Intersect.No;
429                     if (i > 0) inter = cast(Intersect) i;
430                     if (i <= 0) inners &= -i;
431                     max_i = max(max_i, inter);
432                     min_i = min(min_i, inter);
433     
434                     i = if_intersect ([v2.x, v2.y], [v3.x, v3.y], [v1.x, v1.y],
435                         [box[0], box[1], box[3], box[4]], __debug);
436                     inter = Intersect.No;
437                     if (i > 0) inter = cast(Intersect) i;
438                     if (i <= 0) inners &= -i;
439                     max_i = max(max_i, inter);
440                     min_i = min(min_i, inter);
441     
442                     i = if_intersect ([v3.x, v3.y], [v1.x, v1.y], [v2.x, v2.y],
443                         [box[0], box[1], box[3], box[4]], __debug);
444                     inter = Intersect.No;
445                     if (i > 0) inter = cast(Intersect) i;
446                     if (i <= 0) inners &= -i;
447                     max_i = max(max_i, inter);
448                     min_i = min(min_i, inter);
449 
450                     if (__debug) writefln("%s. (%s, %s, %s) points: %s, %s, %s, box: %s", name, 
451                         min_i, max_i, inners, v1, v2, v3, box);
452     
453                     if (min_i > Intersect.No || inners > 0)
454                     {
455                         last_intersect = name;
456                         if (_debug) writefln("%s. %s", min_i, name);
457                         if (!co) return max(min_i, Intersect.Yes);
458                         else intersect = max(min_i, Intersect.Yes, intersect);
459                     }
460                 }
461             }
462         }
463 
464         if (co && cto > cfrom)
465         {
466             collision_cache[co][*sc] ~= CollisionCacheEntry(name, cfrom, cto);
467         }
468         
469         return intersect;
470     }
471 
472     float cx = (box[0] + box[3])/2;
473     float cy = (box[1] + box[4])/2;
474 
475     SC sc = SC(cast(int)round(cx/30), cast(int)round(cy/17));
476 
477     if (cast(void*) co !in collision_cache ||
478         sc !in collision_cache[cast(void*) co])
479     {
480         if (cast(void*)co !in collision_cache)
481             collision_cache[cast(void*)co] = null;
482             
483         if (sc !in collision_cache[cast(void*)co])
484             collision_cache[cast(void*)co][sc] = [];
485 
486         foreach (name, object; co)
487         {
488             //writefln("%s from %s to %s", name, 2, object.length);
489             intersect = max(intersect, check_object(name, object, 2, object.length, cast(void*) co, &sc));
490         }
491     }
492     else
493     {
494         //writefln("%s objects instead of %s", collision_cache[cast(void*) co][sc].length, co.length);
495         foreach (cache_entry; collision_cache[cast(void*) co][sc])
496         {
497             //writefln("*%s from %s to %s", cache_entry.name, cache_entry.from, cache_entry.to);
498             intersect = check_object(cache_entry.name, co[cache_entry.name],
499                 cache_entry.from, cache_entry.to, null, &sc);
500             if (intersect > Intersect.No) return intersect;
501         }
502     }
503             
504     return intersect;    
505 }
506